#include "mainwindow.h"
#include "ui_mainwindow.h"

QVector<QString> disks = GetLoadedDisks();
int CustomIndex=0;
int MultiIndex=0;
QSharedPointer<MTree> Mroot;
Trie Troot;
QThread *scanThread=nullptr;
bool isDrawingTree=0;
int TreemapWidth=1024, TreemapHeight=900;

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::DiskusageAnalyser)
{
    ui->setupUi(this);
    ui->useSuffixCheck->setToolTip("这将允许在文件搜索时从中段开始搜索，但扫描将花费更多的时间");
    ui->ScanProgress->setValue(0);
    ComboRootPathInit();
    connect(ui->FileTree, &QTreeWidget::itemExpanded, this, &MainWindow::onItemExpanded);
    connect(ui->FileTree, &QTreeWidget::itemCollapsed, this, &MainWindow::onItemCollapsed);
    connect(ui->ScanRoot, QOverload<int>::of(&QComboBox::activated),[=](int index)
    {
        if (index == CustomIndex)
        {
            QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),"/home",QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
            if (!dir.isEmpty())
            {
                dir.replace("/","\\\\");
                ui->ScanRoot->setItemText(CustomIndex,dir);
            }
        }
        else if (index == MultiIndex)
        {
            QDialog *dialog = new QDialog(this);
            dialog->setWindowTitle("扫描磁盘选择");
            QVBoxLayout *layout = new QVBoxLayout(dialog);
            QListWidget *listWidget = new QListWidget(dialog);
            layout->addWidget(listWidget);
            QPushButton *okButton = new QPushButton("OK", dialog);
            layout->addWidget(okButton);
            dialog->setLayout(layout);
            listWidget->addItems(QStringList::fromVector(disks));
            listWidget->setSelectionMode(QAbstractItemView::MultiSelection);
            QObject::connect(okButton, &QPushButton::clicked, [=]()
            {
                QList<QListWidgetItem*> selectedItems = listWidget->selectedItems();
                QString multiSelection;
                for (const QListWidgetItem* item : selectedItems)
                {
                    multiSelection += item->text() + " ";
                }
                if (MultiIndex != -1)
                {
                    ui->ScanRoot->setItemText(MultiIndex, "Multi: " + multiSelection);
                }
                dialog->accept();
            });
            dialog->exec();
        }
    });
    connect(ui->SearchStart, &QPushButton::clicked, [=]()
    {
        if(Troot.GetRoot()->children.isEmpty())
        {
            return;
        }
        QString keyword=ui->SearchName->toPlainText();
        QVector<FileInfo> Files;
        if(ui->usePrefixSearch->isChecked())
        {
            if(ui->IgnoreCase->isChecked())
            {
                Files=Troot.PrefixSearchIgnoreCase(keyword);
            }
            else if(ui->useFuzzy->isChecked())
            {
                Files=Troot.FuzzyPrefixSearch(keyword,getMaxCost(keyword));
            }
            else
            {
                Files=Troot.PrefixSearch(keyword);
            }
        }
        else
        {
            if(ui->IgnoreCase->isChecked())
            {
                Files=Troot.SearchIgnoreCase(keyword);
            }
            else if(ui->useFuzzy->isChecked())
            {
                Files=Troot.FuzzySearch(keyword,getMaxCost(keyword));
            }
            else
            {
                Files=Troot.Search(keyword);
            }
        }
        std::sort(Files.begin(), Files.end(), [](FileInfo& a, FileInfo& b)
        {
            return a.getSize() > b.getSize();
        });
        ui->SearchResult->setRowCount(Files.size());
        for (int i = 0; i < Files.size(); ++i)
        {
            QTableWidgetItem* fileNameItem = new QTableWidgetItem(Files[i].getName());
            QTableWidgetItem* filePathItem = new QTableWidgetItem(Files[i].getPath());
            QTableWidgetItem* fileSizeItem = new QTableWidgetItem(showFollowedUnit(Files[i].getSize()));
            ui->SearchResult->setItem(i, 0, fileNameItem);
            ui->SearchResult->setItem(i, 1, filePathItem);
            ui->SearchResult->setItem(i, 2, fileSizeItem);
        }
    });
    connect(ui->SuffixFilter, &QPushButton::clicked, [=]()
    {
        QDialog *dialog = new QDialog(this);
        dialog->setWindowTitle("懒得做了，所以这里其实没有什么用。");
        QVBoxLayout *layout = new QVBoxLayout(dialog);
        QListWidget *listWidget = new QListWidget(dialog);
        layout->addWidget(listWidget);
        QStringList options = {"exe", "apk", "cyd"};
        for (const QString &option : options)
        {
            QWidget *widget = new QWidget(listWidget);
            QHBoxLayout *itemLayout = new QHBoxLayout(widget);
            QCheckBox *checkBox = new QCheckBox(widget);
            QLabel *label = new QLabel(option, widget);
            itemLayout->addWidget(checkBox);
            itemLayout->addWidget(label);
            itemLayout->addStretch(1);
            widget->setLayout(itemLayout);
            QListWidgetItem *item = new QListWidgetItem(listWidget);
            item->setSizeHint(widget->sizeHint());
            listWidget->setItemWidget(item, widget);
        }
        QPushButton *okButton = new QPushButton("OK", dialog);
        layout->addWidget(okButton);
        dialog->setLayout(layout);
        QObject::connect(okButton, &QPushButton::clicked, [=]()
        {
            QString selectedItems;
            for (int i = 0; i < listWidget->count(); ++i)
            {
                QListWidgetItem *item = listWidget->item(i);
                QWidget *widget = listWidget->itemWidget(item);
                QCheckBox *checkBox = widget->findChild<QCheckBox *>();
                if (checkBox && checkBox->isChecked())
                {
                    selectedItems += item->text() + " ";
                }
            }
            dialog->accept();
        });
        dialog->exec();
    });
    connect(ui->IgnoreCase, &QCheckBox::stateChanged, [=](int state)
    {
        if(state == Qt::Checked)
        {
            ui->useFuzzy->setChecked(false);
        }
    });
    connect(ui->useFuzzy, &QCheckBox::stateChanged, [=](int state)
    {
        if(state == Qt::Checked)
        {
            ui->IgnoreCase->setChecked(false);
        }
    });
    connect(ui->donate, &QPushButton::clicked, [=]()
    {
        QDialog *imageDialog = new QDialog(this);
        QLabel *imageLabel = new QLabel(imageDialog);
        QPixmap pixmap(":/images/WeChatShouKuanQRCode.jpg");
        imageLabel->setPixmap(pixmap);
        imageLabel->setFixedSize(pixmap.size());
        QVBoxLayout *layout = new QVBoxLayout(imageDialog);
        layout->addWidget(imageLabel);
        imageDialog->setLayout(layout);
        imageDialog->setWindowTitle("请我喝一瓶可乐");
        imageDialog->exec();
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_ScanStart_clicked()
{
    if ((scanThread && scanThread->isRunning()) || isDrawingTree)
    {
        return;
    }
    if (ui->FileTree != nullptr)
    {
        ui->FileTree->clear();
    }
    ui->ScanProgress->setValue(0);
    setupScanThread();
    QTimer *timer = new QTimer(this);
    setupTimer(timer);
    setupConnections(timer);
    scanThread->start();
    timer->start(300);
}

void MainWindow::on_drawTreemap_clicked()
{
    drawTreeMap(Mroot);
}

void MainWindow::onItemExpanded(QTreeWidgetItem* item)
{
    for (int i = 0; i < item->childCount(); ++i)
    {
        QTreeWidgetItem* childItem = item->child(i);
        if(childItem->text(0)=="其它文件") continue;
                CustomProgressBar* progressBar = new CustomProgressBar(childItem->treeWidget());
        double value = childItem->text(1).toDouble()/100;
        progressBar->setDoubleValue(value);
        progressBar->setFormat("");
        childItem->setData(1, Qt::UserRole, value);
        ui->FileTree->setItemWidget(childItem, 1, progressBar);
    }
}

void MainWindow::onItemCollapsed(QTreeWidgetItem* item)
{
    for (int i = 0; i < item->childCount(); ++i)
    {
        QTreeWidgetItem* childItem = item->child(i);
        CustomProgressBar* progressBar = qobject_cast<CustomProgressBar*>(ui->FileTree->itemWidget(childItem, 1));
        if (progressBar)
        {
            double value = childItem->data(1, Qt::UserRole).toDouble()*100;
            childItem->setText(1, QString::number(value, 'f', 3));
            ui->FileTree->setItemWidget(childItem, 1, nullptr);
        }
    }
}

void MainWindow::onRectangleClicked(QSharedPointer<MTree> root)
{
    drawTreeMap(root);
}

void MainWindow::setupScanThread()
{
    if (scanThread && scanThread->isRunning())
    {
        scanThread->requestInterruption();
        scanThread->wait();
    }
    QString scanRoot = ui->ScanRoot->currentText();
    bool useSuffix = ui->useSuffixCheck->isChecked();
    int index = ui->ScanRoot->currentIndex();
    Mroot = QSharedPointer<MTree>::create(FileInfo(scanRoot, scanRoot));
    Troot = Trie();
    scanThread = QThread::create([=]()
    {
        if (index == 0)
        {
            WindowsScan(Mroot);
        }
        else if (index == MultiIndex)
        {
            MultiScan(parseMultiSelection(scanRoot), Mroot);
        }
        else
        {
            FileScan(scanRoot, Mroot);
        }
    });
}

void MainWindow::setupTimer(QTimer *timer)
{
    connect(timer, &QTimer::timeout, [=]()
    {
        if (ui->ScanProgress->value() < 99)
        {
            ui->ScanProgress->setValue(ui->ScanProgress->value() + 1);
        }
    });
    connect(scanThread, &QThread::finished, this, [=]()
    {
        ui->ScanProgress->setValue(100);
        timer->stop();
    });
}

void MainWindow::setupConnections(QTimer *timer)
{
    connect(scanThread, &QThread::finished, timer, &QTimer::stop);
    connect(scanThread, &QThread::finished, [=]
    {
        scanThread->deleteLater();
        scanThread=nullptr;
    });
    connect(scanThread, &QThread::finished, this, &MainWindow::drawTreeAndSort);
}

void MainWindow::setupTrie()
{
    if(ui->useSuffixCheck->isChecked()) TrieInsertUseSuffix(Mroot);
    else TrieInsert(Mroot);
}

void MainWindow::drawTreeAndSort()
{
    Mroot->Accumulate();
    setupTrie();
    isDrawingTree = 1;
    DrawTree(nullptr, Mroot);
    QTreeWidgetItem *topLevelItem = ui->FileTree->topLevelItem(0);
    double progressValue = topLevelItem->text(1).toDouble() / 100;
    topLevelItem->setText(1, "");
    CustomProgressBar *progressBar = new CustomProgressBar();
    progressBar->setMaximum(100);
    progressBar->setDoubleValue(progressValue);
    topLevelItem->setData(1, Qt::UserRole, progressValue);
    ui->FileTree->setItemWidget(topLevelItem, 1, progressBar);
    TreeSort_Down(1);
    isDrawingTree = 0;
}

void MainWindow::drawTreeMap(QSharedPointer<MTree> root)
{
    Squarify sq;
    QVector<SqInput> inputValues;
    QSharedPointer<MTree> child=root->getChild();
    if(!child) return;
    while(child)
    {
        SqInput item;
        item.ptr=child;
        item.value=(double)child->getFile().getSize();
        inputValues.append(item);
        child=child->getBrother();
    }
    std::sort(inputValues.begin(),inputValues.end(),[](SqInput& a, SqInput& b)
    {
        return a.value>b.value;
    });
    QRectF rect(0, 0, TreemapWidth, TreemapHeight);
    QVector<SqOutput> output = sq.getSquarified(rect, inputValues);
    QDialog *dialog = new QDialog(this);
    dialog->resize(TreemapWidth+30,TreemapHeight+30);
    dialog->setWindowTitle(root->getFile().getPath());
    CustomWidget *customWidget = new CustomWidget(dialog);
    QVBoxLayout *layout = new QVBoxLayout(dialog);
    layout->addWidget(customWidget);
    dialog->setLayout(layout);
    customWidget->setData(output);
    connect(customWidget, &CustomWidget::ItemClicked, this, &MainWindow::onRectangleClicked);
    dialog->exec();
}

QVector<QString> parseMultiSelection(const QString& multiSelection)
{
    QVector<QString> disks;
    QStringList list = multiSelection.split(" ");
    for (const QString& item : list)
    {
        if (item != "Multi:" && !item.isEmpty())
        {
            disks.append(item);
        }
    }
    return disks;
}

void sortAllItems_Down(QTreeWidgetItem* item, int index)
{
    for (int i = 0; i < item->childCount(); i++)
    {
        QTreeWidgetItem* childItem = item->child(i);
        childItem->sortChildren(index, Qt::DescendingOrder);
        sortAllItems_Down(childItem, index);
    }
}

void sortAllItems_Up(QTreeWidgetItem* item, int index)
{
    for (int i = 0; i < item->childCount(); i++)
    {
        QTreeWidgetItem* childItem = item->child(i);
        childItem->sortChildren(index, Qt::AscendingOrder);
        sortAllItems_Down(childItem, index);
    }
}

void MainWindow::TreeSort_Down(int index)
{
    ui->FileTree->sortItems(index,Qt::DescendingOrder);
    for (int i = 0; i < ui->FileTree->topLevelItemCount(); i++)
    {
        QTreeWidgetItem* topLevelItem = ui->FileTree->topLevelItem(i);
        sortAllItems_Down(topLevelItem,index);
    }
}

void MainWindow::TreeSort_Up(int index)
{
    ui->FileTree->sortItems(index,Qt::AscendingOrder);
    for (int i = 0; i < ui->FileTree->topLevelItemCount(); i++)
    {
        QTreeWidgetItem* topLevelItem = ui->FileTree->topLevelItem(i);
        sortAllItems_Up(topLevelItem,index);
    }
}

void MainWindow::ComboRootPathInit()
{
    ui->ScanRoot->addItem("Computer:");
    for(const QString& path : disks)
    {
        ui->ScanRoot->addItem(path);
        CustomIndex++,MultiIndex++;
    }
    ui->ScanRoot->addItem("Multi:");
    CustomIndex++,MultiIndex++;
    ui->ScanRoot->addItem("Custom:");
    CustomIndex++;
}

void MainWindow::DrawTree(QTreeWidgetItem* parentItem, QSharedPointer<MTree> currentNode)
{
    CustomTreeWidgetItem* currentItem = new CustomTreeWidgetItem();
    QStringList infoList = GetInfoList(currentNode);
    int index=0;
    for(const QString& info:infoList) currentItem->setText(index++,info);
    if (parentItem != nullptr)
    {
        parentItem->addChild(currentItem);
    }
    else
    {
        ui->FileTree->addTopLevelItem(currentItem);
    }
    QList<QSharedPointer<MTree>> childFiles;
    QList<QSharedPointer<MTree>> childDirectories;
    QSharedPointer<MTree> childNode = currentNode->getChild();
    while (childNode != nullptr)
    {
        if (childNode->getFile().getType() != "Dir")
        {
            childFiles.append(childNode);
        }
        else
        {
            childDirectories.append(childNode);
        }
        childNode = childNode->getBrother();
    }
    if (childFiles.size() > 3)
    {
        CustomTreeWidgetItem* fileNode = new CustomTreeWidgetItem(currentItem);
        fileNode->setText(0, "其它文件");
        for (QSharedPointer<MTree> file : childFiles)
        {
            DrawTree(fileNode, file);
        }
    }
    else
    {
        for (QSharedPointer<MTree> file : childFiles)
        {
            DrawTree(currentItem, file);
        }
    }
    for (QSharedPointer<MTree> directory : childDirectories)
    {
        DrawTree(currentItem, directory);
    }
}

void TrieInsert(QSharedPointer<MTree> father)
{
    if(!father) return;
    QSharedPointer<MTree> child=father->getChild();
    while(child)
    {
        FileInfo file=child->getFile();
        QString name=file.getName();
        if(file.getType()!="Dir") name.chop(file.getType().length()+1);
        Troot.Insert(name,file);
        TrieInsert(child);
        child=child->getBrother();
    }
}

void TrieInsertUseSuffix(QSharedPointer<MTree> father)
{
    if(!father) return;
    QSharedPointer<MTree> child=father->getChild();
    while(child)
    {
        FileInfo file=child->getFile();
        QString name=file.getName();
        if(file.getType()!="Dir") name.chop(file.getType().length()+1);
        Troot.InsertWithSuffix(name,file);
        TrieInsertUseSuffix(child);
        child=child->getBrother();
    }
}
